#include "scenegraph.h"

using namespace G3;

G3::SCENEGRAPH G3::Scenegraph;

//==============================================================================
// Constructors
//==============================================================================
G3::OBJECT_NODE::OBJECT_NODE (OBJECT_NODE_TYPE nodeType) {
	parent = shared_ptr<OBJECT_NODE>();
	type = nodeType;
	visible = true;
}

G3::OBJECT_NODE::OBJECT_NODE (shared_ptr<OBJECT_NODE> nodeParent, OBJECT_NODE_TYPE nodeType) {
	parent = nodeParent;
	type = nodeType;
	visible = true;
}

G3::DUMMY_NODE::DUMMY_NODE () {
	type = OBJ_NODE_DUMMY;
}

G3::DUMMY_NODE::DUMMY_NODE (shared_ptr<OBJECT_NODE> parent):
	OBJECT_NODE (parent, G3::OBJ_NODE_MESH) {

	type = OBJ_NODE_DUMMY;
}

G3::ANIM_NODE::ANIM_NODE () {
    type = OBJ_ANIM_NODE;
}

G3::ANIM_NODE::ANIM_NODE (shared_ptr<OBJECT_NODE> nodeParent, OBJECT_NODE_TYPE nodeType):
	OBJECT_NODE (nodeParent, nodeType) {

    type = OBJ_ANIM_NODE;
}

//==============================================================================
// Destructors
//==============================================================================
G3::OBJECT_NODE::~OBJECT_NODE () {
	Destroy (true);
}

G3::DUMMY_NODE::~DUMMY_NODE () {
}

G3::ANIM_NODE::~ANIM_NODE () {
}

//==============================================================================
// Attaches Node as a child
//==============================================================================
void G3::OBJECT_NODE::Attach (shared_ptr<OBJECT_NODE> node) {
	if (node)
        children.push_back (node);
}

//==============================================================================
// Detaches Node from the children list
//
//	Returns a pointer to the detached Node when successful
//			NULL in case of an error
//==============================================================================
shared_ptr<OBJECT_NODE> G3::OBJECT_NODE::Detach (shared_ptr<OBJECT_NODE> node) {
	if (!node)
        return shared_ptr<OBJECT_NODE>();

	vector<shared_ptr<OBJECT_NODE> >::iterator i = FindChild (node);
	shared_ptr<OBJECT_NODE> child;

	if (i != children.end ()) {
		child = *i;
		children.erase (i);
		child->parent = shared_ptr<OBJECT_NODE>();
	}
	return child;
}

//==============================================================================
// Removes Node from the children list
//==============================================================================
void G3::OBJECT_NODE::Remove (shared_ptr<OBJECT_NODE> node) {
	if (node) {
        vector<shared_ptr<OBJECT_NODE> >::iterator i = FindChild (node);

        if (i != children.end ())
            children.erase (i);
	}
}

//==============================================================================
// Finds a child
//
//	Returns Children.end () when the child was not found
//			Vector iterator when the child was found
//==============================================================================
vector<shared_ptr<OBJECT_NODE> >::iterator G3::OBJECT_NODE::FindChild (shared_ptr<OBJECT_NODE> child) {
	if (!child)
        return children.end ();

	return find (children.begin (), children.end (), child);
}

//==============================================================================
// Attaches all children to the Parent node
//
//	NOTE:: Also clears the current node's children list
//==============================================================================
void G3::OBJECT_NODE::UploadChildren () {
	if (parent != NULL && !children.empty ()) {
		for (int i=0; i<(int)children.size (); i++) {
			children [i]->parent = parent;
			parent->children.push_back (children [i]);
		}
		children.clear ();
	}
}

//==============================================================================
// Attaches parent as a child
//
//	NOTE:: At first it detaches the parent from the tree and then attaches it
//		   as a child node
//==============================================================================
void G3::OBJECT_NODE::DownloadParent () {
    vector<shared_ptr<OBJECT_NODE> >::iterator it;

	if (parent != NULL) {
		children.push_back (parent);
		it = parent->parent->FindChild (parent);

		if (it != parent->parent->children.end ())
            (*parent->parent->FindChild (parent)) = shared_ptr<OBJECT_NODE> (this);

		parent = parent->parent;
	}
}

//==============================================================================
// Destroys this node
//
//		bool DestroyChildren		Whether or not to also destroy children nodes
//				true				(Default), destroyes this node along with its children
//				false				Uploads children before destroying this node
//==============================================================================
void G3::OBJECT_NODE::Destroy (bool destroyChildren) {
	if (destroyChildren) {
		children.clear ();
		parent = shared_ptr<OBJECT_NODE>();
		type = OBJ_NODE_DUMMY;
	} else {
		UploadChildren ();
		parent = shared_ptr<OBJECT_NODE>();
		type = OBJ_NODE_DUMMY;
	}
}

//==============================================================================
// Gets the type of this node
//==============================================================================
OBJECT_NODE_TYPE G3::OBJECT_NODE::GetNodeType () {
	return type;
}

//==============================================================================
// Renders the dummy node
//==============================================================================
void G3::DUMMY_NODE::Render (float elapsedTime) {
	int i = 0;

	if (!visible)
		return;

	for (i=0; i<(int)children.size (); i++) {
		if (children [i] != NULL)
			children [i]->Render (elapsedTime);
	}
}

//==============================================================================
// Updates the dummy node
//==============================================================================
void G3::DUMMY_NODE::Update (float elapsedTime) {
	int i = 0;

	// Update children
	for (i=0; i<(int)children.size (); i++) {
		if (children [i] != NULL)
			children [i]->Update (elapsedTime);
	}
}

//==============================================================================
// Updates children' WorldMatrices
//==============================================================================
void G3::ANIM_NODE::PropagateMatrix () {
	if (!children.empty ()) {
		for (int i=0; i<(int)children.size (); i++) {
			if (children [i] != NULL &&
                (children [i]->GetNodeType () == OBJ_ANIM_NODE ||
                 children [i]->GetNodeType () == OBJ_NODE_MESH)) {

				(dynamic_pointer_cast<ANIM_NODE> (children [i]))->world_matrix = G3::MultMatrices (world_matrix, model_matrix);
				(dynamic_pointer_cast<ANIM_NODE> (children [i]))->PropagateMatrix ();
			}
		}
	}
}

//==============================================================================
// Attaches a controller
//==============================================================================
void G3::ANIM_NODE::AttachController (CONTROLLER *ctrl) {
	if (ctrl)
        controllers.push_back (ctrl);
}

//==============================================================================
// Detaches a controller
//==============================================================================
void G3::ANIM_NODE::DetachController (CONTROLLER *ctrl) {
	vector<CONTROLLER *>::iterator it;

	if (ctrl) {
		it = find (controllers.begin (), controllers.end (), ctrl);

		if (it != controllers.end ())
			controllers.erase (it);
	}
}

//==============================================================================
// Renders the animation node
//==============================================================================
void G3::ANIM_NODE::Render (float elapsedTime) {
	int i = 0;

	if (!visible)
		return;

	for (i=0; i<(int)children.size (); i++) {
		if (children [i] != NULL)
			children [i]->Render (elapsedTime);
	}
}

//==============================================================================
// Updates the animation node
//==============================================================================
void G3::ANIM_NODE::Update (float elapsedTime) {
	int i = 0;

	// Update controllers
	for (i=0; i<(int)controllers.size (); i++) {
		if (controllers [i]) {
			controllers [i]->Update (elapsedTime);
			// Apply transformation controllers
			if (controllers [i]->GetType () == CT_ROTATION)
				model_matrix = model_matrix * ((ROTATION_CTRL *) controllers [i])->matrix;
			else if (controllers [i]->GetType () == CT_TRANSLATION)
				model_matrix = model_matrix * ((TRANSLATION_CTRL *) controllers [i])->matrix;
			else if (controllers [i]->GetType () == CT_SCALE)
				model_matrix = model_matrix * ((SCALE_CTRL *) controllers [i])->matrix;
		}
	}

	// Update children
	for (i=0; i<(int)children.size (); i++) {
		if (children [i] != NULL)
			children [i]->Update (elapsedTime);
	}
}

//==============================================================================
// Renders the whole scenegraph
//==============================================================================
void G3::SCENEGRAPH::Render (float elapsedTime) {
	root.Render (elapsedTime);
}

//==============================================================================
// Updates the whole scenegraph
//==============================================================================
void G3::SCENEGRAPH::Update (float elapsedTime) {
	root.Update (elapsedTime);
}
